#!/usr/bin/env python

from pytamp import *
import planningproblem
import action
import output
import getopt

def load_tgf(file):
    # maps id strings to data
    nodes = dict({})
    # maps (id1,id2) strings to data
    edges = dict({})

    f = open(file,'r')
    readingEdges = False
    index=0
    for line in f.readlines():
        index += 1
        line = line.lstrip().rstrip()
        if not readingEdges:
            if line[0]=='#':
                readingEdges=True
            else:
                (id,space,line) = line.partition(' ')
                if space=='':
                    id = line
                if id in nodes:
                    raise ValueError("Duplicate node %s on line %d"%(id,index))
                #save it
                nodes[id] = line
        else:
            if len(line)==0:
                #done
                break
            (id1,space,line) = line.partition(' ')
            if space=='':
                raise IOError("Couldn't read first id from line %d"%(index))
            (id2,space,line) = line.partition(' ')
            if id2=='' and space=='':
                id2=line
            if (id1,id2) in edges:
                raise ValueError("Duplicate edge (%s,%s) on line %d"%(id1,id2,index))
            #save it
            edges[(id1,id2)]=line
    f.close()
    return nodes,edges

def makedict(**kws):
    return kws


class LoadedPlan:
    def __init__(self,problem,tgffile=None):
        self.problem = problem
        if tgffile:
            self.load(tgffile)
    def load(self,tgffile):
        nodes,edges = load_tgf(tgffile)
        #Parse the TGF file
        self.nodes = ['']*len(nodes)
        for n,s in nodes.iteritems():
            self.nodes[int(n)-1]=eval(s)
        self.adjlist = []
        #need this construction because otherwise all dicts are shared
        for i in xrange(len(nodes)):
            self.adjlist.append({})
        edgere = re.compile('(\w+)\((.+)\);(.+)')
        for (id1,id2),s in edges.iteritems():
            match = edgere.match(s.strip())
            aname,aargs,subpath = match.groups()
            #turn args string into a dictionary
            params = eval('makedict('+aargs+')')
            a = [a for a in self.problem.actions if a.name==aname][0]
            ba = action.BoundAction(a,params)
            path = None
            if subpath.strip() in ['failed','active','complete']:
                pass
            else:
                path = eval(subpath)
            self.adjlist[int(id1)-1][int(id2)-1]=(ba,path)

    def pretty_print_dfs(self,n,tab,indent,tree=False):
        if self.visited[n]: return
        self.visited[n]=True
        output.pretty_print_state(self.nodes[n],tab*indent)
        indent += 1
        for j,(a,p) in sorted(self.adjlist[n].iteritems()):
            if not tree: indent = 1
            if a != None:
                output.pretty_print_action(a,tab*indent)
            if p != None:
                output.pretty_print_subplan_solution(p,tab*indent)
            if not tree: indent = 0
            self.pretty_print_dfs(j,tab,indent)

    def pretty_print(self,tab='  ',tree=False):
        self.visited = [False]*len(self.nodes)
        # do a DFS
        for n in xrange(len(self.nodes)):
            self.pretty_print_dfs(n,tab,0)

    def output(self,outputter,drawtail=False):
        inedge = [False]*len(self.nodes)
        outedge = [False]*len(self.nodes)
        outputter.output_start()
        for n,state in enumerate(self.nodes):
            for j,(a,path) in self.adjlist[n].iteritems():
                outedge[n] = True
                inedge[j] = True
                print 'edge',(n,j)
                if drawtail:
                    outputter.output_state(self.nodes[j],j)
                    outputter.output_action(self.nodes[j],a,path,j)
                else:
                    outputter.output_state(self.nodes[n],j)
                    outputter.output_action(self.nodes[n],a,path,j)
        for n,state in enumerate(self.nodes):
            if (not inedge[n] and not drawtail) or (not outedge[n] and drawtail):
                outputter.output_state(state,n)
        outputter.output_stop()

if __name__ == '__main__':
    #Ugly getopt version
    try:
        optlist, args = getopt.getopt(sys.argv[1:],'hitp',['help','interactive','tail','print'])
    except getopt.GetoptError, err:
        print str(err) 
        usage()
        sys.exit(2)

    problemfile = args[0]
    tgffile = args[1]
    interactive = False
    drawtailstate = False
    doprint = False
    for o, a in optlist:
        if o in ("-i","--interactive"):
            interactive = True
        if o in ("-t","--tail"):
            drawtailstate = True
        if o in ("-p","--print"):
            doprint = True
        elif o in ("-h", "--help"):
            usage()
            sys.exit()
        else:
            assert False, "unhandled option"
    
    problem,settings,outputter = parse_problem(problemfile)
    plan = LoadedPlan(problem,tgffile)

    if not interactive:
        if outputter==None or doprint:
            plan.pretty_print()
        else:
            plan.output(outputter,drawtailstate)
